#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

#include "argopt.h"
#define argopt_ctl_optbuf_len(ctl) (ctl->len * 2 + 1)
/// @brief 获取getopt所需解析字符串
/// @param ctl
static inline int __getopt_optstring(argopt_ctl_t *ctl)
{
	ctl->optbuf = malloc(argopt_ctl_optbuf_len(ctl));
	if (ctl->optbuf == NULL)
	{
		fprintf(stderr, "info ctl->optbuf malloc is null!\n");
		return -1;
	}
	memset(ctl->optbuf, 0, argopt_ctl_optbuf_len(ctl));
	size_t i = 0;
	for (i = 0; i < ctl->len; ++i)
	{
		strncat(ctl->optbuf, &(ctl->arr[i].opt), 1);
		if (ctl->arr[i].state & ARGOPT_DATAENABLE)
		{
			strcat(ctl->optbuf, ":");
		}
	}
	// fprintf(stdout, "info: ctl->optbuf: %s\n", ctl->optbuf);
	return 0;
	
}

int argopt_ctl_init(argopt_ctl_t *ctl, argopt_t *arr, size_t len,int argc,char *argv[])
{
	if (ctl == NULL || arr == NULL)
		return -1;
	ctl->arr = arr;
	ctl->len = len;
	if (__getopt_optstring(ctl))
		return -1;

	int opt = 0;
	int err = 0;
	// 解析参数并设置
	while ((opt = getopt(argc, argv, ctl->optbuf)) != -1)
	{
		for (size_t i = 0; i < ctl->len; i++)
		{
			argopt_t *self = &(ctl->arr[i]);
			if (self == NULL || self->action_impl == NULL)
				continue;
			if (self->opt == opt)
			{
				if (self->data != NULL) // 重新设置数据
				{
					if (self->free_impl != NULL)
					{
						self->free_impl(self);
						self->data = NULL;
					}
				}
				int e = self->action_impl(ctl, self, optarg);
				if (e != 0)
				{
					err += e;
					fprintf(stderr, "info: %c action err:%d totle err:%d\n", opt, e, err);
				}
				if (self->state & ARGOPT_DATAENABLE && self->data == NULL)
				{
					fprintf(stderr, "info: %c set ARGOPT_DATAENABLE but data is null! if you save arg to other you can continue this info\n", self->opt);
				}
			}
		}
	}
	return err;
}

void *argopt_ctl_getdata(argopt_ctl_t *ctl, char opt)
{
	if (ctl == NULL)
		return NULL;
	
	for (size_t i = 0; i < ctl->len; i++)
	{
		argopt_t *self = &(ctl->arr[i]);
		if (self != NULL && opt == self->opt)
			return self->data;
	}
	fprintf(stderr, "info :not found opt:%c\n", opt);
	return NULL;
}

void argopt_ctl_setdata(argopt_ctl_t *ctl, char opt, void *data)
{
	if (ctl == NULL)
		return;
	for (size_t i = 0; i < ctl->len; i++)
	{
		argopt_t *self = &(ctl->arr[i]);
		if (self->opt == opt)
		{
			if (self->data != NULL && self->free_impl != NULL)
				self->free_impl(self);
			self->data = data;
		}
	}
}

void argopt_setdata(argopt_t *argopt, void *data)
{
	if (argopt == NULL)
		return;
	if (argopt->data != NULL && argopt->free_impl != NULL)
		argopt->free_impl(argopt);	
	argopt->data = data;
}

void argopt_ctl_deinit(argopt_ctl_t *ctl)
{
	if (ctl == NULL)
		return;
	if (ctl->optbuf != NULL)
		free(ctl->optbuf);
	for (size_t i = 0; i < ctl->len; i++)
	{
		argopt_t *self = &(ctl->arr[i]);
		if (self == NULL)
			continue;
		if (self->data != NULL)
		{
			if (self->free_impl != NULL)
				self->free_impl(self);
			else
				fprintf(stderr, "info: %c data is not null buf free_impl is null!\n", self->opt);
		}
	}
}